package com.wang.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.wang.config.RabbitMQConfig;
import com.wang.enums.BizCodeEnum;
import com.wang.enums.ProductOrderStateEnum;
import com.wang.enums.StockTaskStateEnum;
import com.wang.exception.BizException;
import com.wang.feign.ProductOrderFeignService;
import com.wang.mapper.ProductMapper;
import com.wang.mapper.ProductTaskMapper;
import com.wang.model.ProductDO;
import com.wang.model.ProductMessage;
import com.wang.model.ProductTaskDO;
import com.wang.request.LockProductRequest;
import com.wang.request.OrderItemRequest;
import com.wang.service.ProductService;
import com.wang.util.JsonData;
import com.wang.vo.ProductVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
import java.util.stream.Collectors;

@Service
@Slf4j
public class ProductServiceImpl implements ProductService {
    @Autowired
    private ProductMapper productMapper;

    @Autowired
    private ProductTaskMapper productTaskMapper;

    @Autowired
    private RabbitMQConfig rabbitMQConfig;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private ProductOrderFeignService productOrderFeignService;

    /**
     * 分页查询商品列表
     *
     * @param page
     * @param size
     * @return
     */
    @Override
    public Map<String, Object> pageProductList(Integer page, Integer size) {
        Page<ProductDO> pageInfo = new Page<>(page, size);
        Page<ProductDO> productDOPage = productMapper.selectPage(pageInfo, null);

        Map<String, Object> result = new HashMap<>();
        result.put("total_record", productDOPage.getTotal());
        result.put("total_page", productDOPage.getPages());
        result.put("current_data", productDOPage.getRecords().stream().map(obj -> beanProcess(obj)).collect(Collectors.toList()));

        return result;
    }

    /**
     * 根据商品ID查询商品详情
     *
     * @param productId
     * @return
     */
    @Override
    public ProductVO findByProductId(Long productId) {
        ProductDO productDO = productMapper.selectById(productId);
        return beanProcess(productDO);
    }

    /**
     * 根据商品ID批量查询商品详情
     *
     * @param productIds
     * @return
     */
    @Override
    public List<ProductVO> findBYProductIdBatch(List<Long> productIds) {
        List<ProductDO> productDOS = productMapper.selectList(new QueryWrapper<ProductDO>().in("id", productIds));

        List<ProductVO> collect = productDOS.stream().map(obj -> beanProcess(obj)).collect(Collectors.toList());
        return collect;

    }

    /**
     * 锁定商品库存
     * 1).批量查询商品信息
     * 2).依次锁定商品库存（库存是否足够），并发送延迟消息（延迟消息要一个一个发送）
     * <p>
     * 一次性锁定库存并发送一个延迟消息可能导致以下问题：
     * 事务不一致：如果部分商品库存更新失败，但消息已发送，后续补偿机制会变得复杂。
     * 无法精确控制：一个消息可能代表多个商品的锁定操作，一旦出现异常（如超时、失败），难以定位具体是哪个商品出错。
     * 幂等性问题：接收方处理多个商品的延迟消息时，难以保证幂等性。
     * 因此，逐个发送延迟消息可以确保每条消息都对应一次明确的操作，便于系统维护和错误排查。
     *
     * @param lockProductRequest
     * @return
     */
    @Override
    public JsonData lockProductStock(LockProductRequest lockProductRequest) {
        String outTradeNo = lockProductRequest.getOrderOutTradeNo();
        List<OrderItemRequest> orderItemList = lockProductRequest.getOrderItemList();

        List<Long> productIdList = orderItemList.stream().map(OrderItemRequest::getProductId).collect(Collectors.toList());
        //批量查询商品信息
        List<ProductVO> productVOList = productMapper.findProductsByIdBatch(productIdList);


        //分组
        Map<Long, ProductVO> productMap = productVOList.stream().collect(Collectors.toMap(ProductVO::getId, Function.identity()));

        //依次锁定商品库存并发送延迟消息
        for (OrderItemRequest item : orderItemList) {
            //锁定商品记录
            int rows = productMapper.lockProductStock(item.getProductId(), item.getBuyNum());
            if (rows != 1) {
                throw new BizException(BizCodeEnum.ORDER_CONFIRM_LOCK_PRODUCT_FAIL);
            } else {
                //插入商品product_task
                ProductVO productVO = productMap.get(item.getProductId());
                ProductTaskDO productTaskDO = new ProductTaskDO();
                productTaskDO.setBuyNum(item.getBuyNum());
                productTaskDO.setLockState(StockTaskStateEnum.LOCK.name());
                productTaskDO.setProductId(item.getProductId());
                productTaskDO.setProductName(productVO.getTitle());
                productTaskDO.setOutTradeNo(outTradeNo);
                //Mybatis plus的添加会自动返回主键ID，所以productTaskDO对象中已经是有了主键的
                productTaskMapper.insert(productTaskDO);
                log.info("商品库存锁定成功:{}", productTaskDO);

                // 发送MQ延迟消息
                ProductMessage mes = new ProductMessage();
                mes.setOutTradeNo(outTradeNo);
                mes.setTaskId(productTaskDO.getId());

                rabbitTemplate.convertAndSend(rabbitMQConfig.getEventExchange(), rabbitMQConfig.getStockReleaseQueue(), mes);

                log.info("商品库存锁定延迟消息发送成功:{}", mes);
            }

        }
        return JsonData.buildSuccess();
    }

    /**
     * 释放商品库存
     *
     * @param productMessage
     * @return
     */
    @Override
    public boolean releaseCouponRecord(ProductMessage productMessage) {
        //查询商品工作单（商品task表）数据状态
        Long taskId = productMessage.getTaskId();
        ProductTaskDO productTaskDO = productTaskMapper.selectById(taskId);


        //只有锁定状态才会进行处理  三种状态：lock锁定、finish完成、cancel取消）
        if (StockTaskStateEnum.LOCK.name().equalsIgnoreCase(productTaskDO.getLockState())) {

            //查看订单支付状态  （三种支付状态：NEW 未支付订单,PAY已经支付订单,CANCEL超时取消订单）
            JsonData jsonData = productOrderFeignService.queryProductOrderState(productMessage.getOutTradeNo());
            if (jsonData.getCode() == 0) {//查询成功
                String state = jsonData.getData().toString();

                //如果支付状态为NEW，则返回给消息队，列重新投递
                if(ProductOrderStateEnum.NEW.name().equalsIgnoreCase(state)){
                    log.warn("订单状态是NEW,返回给消息队列，重新投递:{}", productMessage);
                    return false;
                }

                //如果支付状态为PAY
                if(ProductOrderStateEnum.PAY.name().equalsIgnoreCase(state)){
                    //修改task表对应的数据状态为FINISH
                    productTaskMapper.update(productTaskDO, new QueryWrapper<ProductTaskDO>().eq("id", taskId));
                    log.info("订单已经支付，修改库存锁定工作单FINISH状态:{}", productMessage);
                    return true;
                }

                //订单不存在，或者订单被取消，确认消息,修改task状态为CANCEL,恢复优惠券使用记录为NEW
                log.warn("订单不存在，或者订单被取消，确认消息,修改task状态为CANCEL,恢复优惠券使用记录为NEW,message:{}",productMessage);
                productTaskDO.setLockState(StockTaskStateEnum.CANCEL.name());

                productTaskMapper.update(productTaskDO,new QueryWrapper<ProductTaskDO>().eq("id",productMessage.getTaskId()));

                //恢复商品锁定库存
                productMapper.releaseProductStock(productTaskDO.getProductId(),productTaskDO.getBuyNum());

            }else{
                log.warn("订单支付状态查询失败，订单号：{}", productMessage.getOutTradeNo());
                return true;
            }
        }


        return false;
    }

    private ProductVO beanProcess(ProductDO obj) {
        ProductVO productVO = new ProductVO();
        BeanUtils.copyProperties(obj, productVO);
        //productVO中的库存是剩余库存   所以要用总库存减去锁定库存
        productVO.setStock(obj.getStock() - obj.getLockStock());
        return productVO;
    }
}
